/**
 * gl-marching-squares - 高性能等值线提取库
 * 支持ImageData输入、多模块格式及等值线闭合控制
 */

// ==========================================
// 类型定义
// ==========================================
export type Point = [number, number];
export type ContourSegment = [Point, Point];
export type ContourResult = ContourSegment[] | Float32Array;

export interface ConvertResult {
  data: Float32Array;
  width: number;
  height: number;
}

export interface ContourOptions {
  skipValidation?: boolean;
  returnFloat32?: boolean;
  epsilon?: number;
  enableSparse?: boolean;
  sparseThreshold?: number;
  enableClosing?: boolean;
  closingEpsilon?: number;
}

// ==========================================
// 常量与预计算数据
// ==========================================
const EDGE_PAIRS = new Uint8Array([
  0x00, 0x30, 0x01, 0x31, 0x12, 0x30, 0x12, 0x02, 0x32, 0x23, 0x02, 0x01, 0x23, 0x13, 0x12, 0x02, 0x01, 0x00
]);

const EDGE_COUNT = [0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0];
const EDGE_DATA = [
  [0, 0, 1, 0], [1, 0, 1, 1], [1, 1, 0, 1], [0, 1, 0, 0]
];

const DEFAULT_OPTIONS: ContourOptions = {
  skipValidation: false,
  returnFloat32: false,
  epsilon: 1e-6,
  enableSparse: true,
  sparseThreshold: undefined,
  enableClosing: false,
  closingEpsilon: undefined
};

// ==========================================
// 工具函数
// ==========================================
/**
 * 创建稀疏掩码以优化计算
 */
function createSparseMask(
  data: Float32Array,
  width: number,
  height: number,
  threshold: number
): {
  minRow: number;
  maxRow: number;
  rowMasks: { minCol: number; maxCol: number }[];
} {
  const rowMasks = new Array(height);
  let minRow = height;
  let maxRow = -1;
  const thresholdSquared = threshold * threshold;

  for (let y = 0; y < height; y++) {
    const rowStart = y * width;
    let hasValid = false;
    let minCol = width;
    let maxCol = -1;
    let allAbove = true;
    let allBelow = true;
    
    for (let x = 0; x < width; x += 4) {
      const val = data[rowStart + x];
      allAbove &&= val >= threshold;
      allBelow &&= val < threshold;
      if (!allAbove && !allBelow) break;
    }

    if (allAbove || allBelow) {
      rowMasks[y] = { minCol: width, maxCol: -1 };
      continue;
    }

    for (let x = 0; x < width; x++) {
      const val = data[rowStart + x];
      const isNearThreshold = Math.abs(val - threshold) < thresholdSquared;
      
      if (isNearThreshold) {
        hasValid = true;
        if (x < minCol) minCol = x;
        if (x > maxCol) maxCol = x;
      }
    }

    if (hasValid) {
      rowMasks[y] = { 
        minCol: Math.max(0, minCol - 1),
        maxCol: Math.min(width - 1, maxCol + 1)
      };
      if (y < minRow) minRow = y;
      if (y > maxRow) maxRow = y;
    } else {
      rowMasks[y] = { minCol: width, maxCol: -1 };
    }
  }

  if (minRow === height) {
    minRow = 0;
    maxRow = height - 1;
  }

  return { minRow, maxRow, rowMasks };
}

/**
 * 等值线闭合核心算法
 */
function closeContours(
  segments: ContourSegment[],
  epsilon: number
): ContourSegment[] {
  if (segments.length < 2) return segments;

  const endpointMap = new Map<string, Point[]>();
  const epsilonSquared = epsilon * epsilon;

  const getPointKey = (p: Point): string => {
    return `${Math.round(p[0] / epsilon)},${Math.round(p[1] / epsilon)}`;
  };

  segments.forEach(([p1, p2]) => {
    const key1 = getPointKey(p1);
    const key2 = getPointKey(p2);
    
    if (!endpointMap.has(key1)) endpointMap.set(key1, []);
    if (!endpointMap.has(key2)) endpointMap.set(key2, []);
    
    endpointMap.get(key1)!.push(p1);
    endpointMap.get(key2)!.push(p2);
  });

  const closedSegments = [...segments];
  const processed = new Set<number>();

  for (let i = 0; i < segments.length; i++) {
    if (processed.has(i)) continue;
    
    const [start, end] = segments[i];
    const endKey = getPointKey(end);
    const candidates = endpointMap.get(endKey) || [];

    for (let j = 0; j < segments.length; j++) {
      if (i === j || processed.has(j)) continue;
      
      const [otherStart] = segments[j];
      const dx = otherStart[0] - end[0];
      const dy = otherStart[1] - end[1];
      
      if (dx * dx + dy * dy < epsilonSquared) {
        closedSegments.push([end, otherStart]);
        processed.add(i);
        processed.add(j);
        break;
      }
    }
  }

  return closedSegments;
}

// ==========================================
// 数据转换函数
// ==========================================
/**
 * 将二维数组转换为Float32Array格式
 */
export function convertToFloat32Array(grid: number[][]): ConvertResult {
  if (!grid || grid.length < 2 || !grid[0] || grid[0].length < 2) {
    throw new Error("网格必须≥2x2");
  }

  const height = grid.length;
  const width = grid[0].length;
  const data = new Float32Array(width * height);
  let idx = 0;

  for (let y = 0; y < height; y++) {
    const row = grid[y];
    for (let x = 0; x < width; x++) {
      data[idx++] = row[x];
    }
  }

  return { data, width, height };
}

/**
 * 将ImageData转换为Float32Array网格（支持多通道提取）
 */
export function convertImageDataToGrid(
  imageData: ImageData,
  channel: 'gray' | 'r' | 'g' | 'b' | 'a' = 'gray'
): ConvertResult {
  if (!imageData || !imageData.data || imageData.width < 2 || imageData.height < 2) {
    throw new Error("ImageData必须≥2x2像素");
  }

  const { width, height, data } = imageData;
  const gridData = new Float32Array(width * height);
  let idx = 0;

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const pixelIdx = (y * width + x) * 4; // RGBA每个像素占4个位置
      let value: number;

      switch (channel) {
        case 'r':
          value = data[pixelIdx]; // 红色通道
          break;
        case 'g':
          value = data[pixelIdx + 1]; // 绿色通道
          break;
        case 'b':
          value = data[pixelIdx + 2]; // 蓝色通道
          break;
        case 'a':
          value = data[pixelIdx + 3]; // 透明通道
          break;
        case 'gray':
          // 灰度值计算公式：符合人眼感知的亮度转换
          value = 0.299 * data[pixelIdx] + 
                  0.587 * data[pixelIdx + 1] + 
                  0.114 * data[pixelIdx + 2];
          break;
        default:
          value = 0;
      }

      gridData[idx++] = value;
    }
  }

  return { data: gridData, width, height };
}

// ==========================================
// 核心算法
// ==========================================
/**
 * 生成单阈值等值线
 */
export function generateContour(
  data: Float32Array,
  width: number,
  height: number,
  threshold: number,
  options: ContourOptions = {}
): ContourResult {
  const opts = { ...DEFAULT_OPTIONS, ...options };
  const epsilon = opts.epsilon!;
  const returnFloat32 = opts.returnFloat32!;
  const enableSparse = opts.enableSparse!;
  const sparseThreshold = opts.sparseThreshold ?? threshold;
  const enableClosing = opts.enableClosing!;
  const closingEpsilon = opts.closingEpsilon ?? epsilon;

  // 数据验证
  if (!opts.skipValidation) {
    if (width < 2 || height < 2) throw new Error(`网格太小: ${width}x${height}`);
    if (data.length !== width * height) throw new Error(`数据长度不匹配: ${data.length} vs ${width*height}`);
  }

  // 稀疏网格预处理
  let mask = { minRow: 0, maxRow: height - 1, rowMasks: [] as { minCol: number; maxCol: number }[] };
  if (enableSparse) {
    mask = createSparseMask(data, width, height, sparseThreshold);
  } else {
    mask.rowMasks = Array.from({ length: height }, () => ({ minCol: 0, maxCol: width - 1 }));
    mask.minRow = 0;
    mask.maxRow = height - 1;
  }

  const { minRow, maxRow, rowMasks } = mask;
  const effectiveMinRow = Math.max(0, minRow);
  const effectiveMaxRow = Math.min(height - 2, maxRow);
  if (effectiveMinRow > effectiveMaxRow) {
    return returnFloat32 ? new Float32Array(0) : [];
  }

  // 第一遍：统计总线段数
  let totalSegments = 0;
  let rowStartIdx = effectiveMinRow * width;
  
  for (let y = effectiveMinRow; y <= effectiveMaxRow; y++) {
    const { minCol, maxCol } = rowMasks[y];
    if (minCol >= maxCol) continue;

    const effectiveMinCol = Math.max(0, minCol);
    const effectiveMaxCol = Math.min(width - 2, maxCol);
    if (effectiveMinCol > effectiveMaxCol) continue;

    let v0Idx = rowStartIdx + effectiveMinCol;
    let v2Idx = rowStartIdx + width + effectiveMinCol + 1;

    for (let x = effectiveMinCol; x <= effectiveMaxCol; x++) {
      const code = (data[v0Idx] >= threshold ? 8 : 0) |
                   (data[v0Idx + 1] >= threshold ? 4 : 0) |
                   (data[v2Idx] >= threshold ? 2 : 0) |
                   (data[v2Idx - 1] >= threshold ? 1 : 0);
      
      totalSegments += EDGE_COUNT[code];
      v0Idx++;
      v2Idx++;
    }
    rowStartIdx += width;
  }

  // 第二遍：计算等值线
  const segments: ContourSegment[] = new Array(totalSegments);
  let resultIdx = 0;
  rowStartIdx = effectiveMinRow * width;
  
  for (let y = effectiveMinRow; y <= effectiveMaxRow; y++) {
    const { minCol, maxCol } = rowMasks[y];
    if (minCol >= maxCol) {
      rowStartIdx += width;
      continue;
    }

    const effectiveMinCol = Math.max(0, minCol);
    const effectiveMaxCol = Math.min(width - 2, maxCol);
    if (effectiveMinCol > effectiveMaxCol) {
      rowStartIdx += width;
      continue;
    }

    let v0Idx = rowStartIdx + effectiveMinCol;
    let v1Idx = rowStartIdx + effectiveMinCol + 1;
    let v3Idx = rowStartIdx + width + effectiveMinCol;
    let v2Idx = v3Idx + 1;

    for (let x = effectiveMinCol; x <= effectiveMaxCol; x++) {
      const v0 = data[v0Idx];
      const v1 = data[v1Idx];
      const v2 = data[v2Idx];
      const v3 = data[v3Idx];
      
      const code = (v0 >= threshold ? 8 : 0) |
                   (v1 >= threshold ? 4 : 0) |
                   (v2 >= threshold ? 2 : 0) |
                   (v3 >= threshold ? 1 : 0);

      if (code === 0 || code === 15) {
        v0Idx++;
        v1Idx++;
        v3Idx++;
        v2Idx++;
        continue;
      }

      const segCount = EDGE_COUNT[code];
      const edgeOffset = code === 5 ? 2 : (code === 10 ? 2 : 1);
      let edgePtr = code * edgeOffset;
      const cellX = x;
      const cellY = y;
      
      for (let s = 0; s < segCount; s++) {
        const edgeA = EDGE_PAIRS[edgePtr] >> 4;
        const edgeB = EDGE_PAIRS[edgePtr] & 0x0F;
        edgePtr++;

        const ea = EDGE_DATA[edgeA];
        const sAx = cellX + ea[0];
        const sAy = cellY + ea[1];
        const eAx = cellX + ea[2];
        const eAy = cellY + ea[3];
        const aVal = edgeA === 0 ? v0 : (edgeA === 1 ? v1 : (edgeA === 2 ? v2 : v3));

        const eb = EDGE_DATA[edgeB];
        const sBx = cellX + eb[0];
        const sBy = cellY + eb[1];
        const eBx = cellX + eb[2];
        const eBy = cellY + eb[3];
        const bVal = edgeB === 0 ? v0 : (edgeB === 1 ? v1 : (edgeB === 2 ? v2 : v3));

        // 插值计算
        let tA, tB;
        const diffA = bVal - aVal;
        const diffB = bVal - aVal;
        
        if ((aVal >= threshold && bVal >= threshold) || (aVal < threshold && bVal < threshold) || Math.abs(diffA) < epsilon) {
          tA = 0.5;
        } else {
          tA = (threshold - aVal) / diffA;
        }
        const x1 = sAx + tA * (eAx - sAx);
        const y1 = sAy + tA * (eAy - sAy);

        if ((aVal >= threshold && bVal >= threshold) || (aVal < threshold && bVal < threshold) || Math.abs(diffB) < epsilon) {
          tB = 0.5;
        } else {
          tB = (threshold - aVal) / diffB;
        }
        const x2 = sBx + tB * (eBx - sBx);
        const y2 = sBy + tB * (eBy - sBy);

        segments[resultIdx++] = [[x1, y1], [x2, y2]];
      }

      v0Idx++;
      v1Idx++;
      v3Idx++;
      v2Idx++;
    }
    rowStartIdx += width;
  }

  // 处理等值线闭合
  let finalSegments = segments;
  if (enableClosing && !returnFloat32) {
    finalSegments = closeContours(segments, closingEpsilon);
  }

  // 转换为Float32格式
  if (returnFloat32) {
    const float32Buffer = new Float32Array(finalSegments.length * 4);
    finalSegments.forEach(([[x1, y1], [x2, y2]], idx) => {
      float32Buffer[idx * 4] = x1;
      float32Buffer[idx * 4 + 1] = y1;
      float32Buffer[idx * 4 + 2] = x2;
      float32Buffer[idx * 4 + 3] = y2;
    });
    return float32Buffer;
  }

  return finalSegments;
}

/**
 * 批量生成多阈值等值线
 */
export function generateMultiContours(
  data: Float32Array,
  width: number,
  height: number,
  thresholds: number[],
  options: ContourOptions = {}
): Record<number, ContourResult> {
  const result: Record<number, ContourResult> = {};
  if (thresholds.length === 0) return result;

  const baseOptions = { ...options, skipValidation: false };
  result[thresholds[0]] = generateContour(data, width, height, thresholds[0], baseOptions);
  
  const fastOptions = { ...options, skipValidation: true };
  for (let i = 1; i < thresholds.length; i++) {
    result[thresholds[i]] = generateContour(data, width, height, thresholds[i], fastOptions);
  }

  return result;
}

export default {
  generateContour,
  generateMultiContours,
  convertToFloat32Array,
  convertImageDataToGrid
};
